home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / TC2PROM.ARJ / TCROM.DOC < prev   
Text File  |  1991-05-01  |  14KB  |  344 lines

  1. WRITING ROMABLE CODE IN TURBO C:
  2.  
  3. Updated 2/2/89 for Turbo C 2.0 by the author.
  4.  
  5.     I recently completed a consulting project to design a
  6. special-purpose controller using an embedded 8088 with firmware
  7. in ROM. To create the firmware, I developed a scheme for
  8. generating ROMable code from Turbo C.
  9.  
  10.     The scheme utilizes the following:
  11.  
  12.     1. Rules for writing C code.
  13.  
  14.     2. ROMIZE.C, a program to post-process the Turbo C Compiler
  15. assembler output.
  16.  
  17.     3. INSJUMP.C, a program to patch the ROM binary code to
  18. insert a restart jump instruction.
  19.  
  20.     4. BINTOHEX.C, a program to produce Intel-format files for
  21. downloading ROM images to an EPROM programmer.
  22.  
  23.     5. C0ROMX.ASM, a replacement for C0.ASM, the startup code.
  24.  
  25.     In addition, the scheme requires the Turboc C command-line
  26. compiler, TCC.EXE; an assembler, Microsoft Version 3.0 or later,
  27. or equivalent (I use Microsoft V5.0); TLINK or MS-Link; and
  28. EXE2BIN (more about EXE2BIN later).
  29.  
  30.     The scheme involves the following steps:
  31.  
  32.     1. C code is written following a few rules.
  33.  
  34.     2. TCC is used to translate the C code to assembly.
  35.  
  36.     3. ROMIZE is used to perform certain modifications to the
  37. assembly code.
  38.  
  39.     4. The modified assembly code is assembled into a .OBJ file.
  40.  
  41.     5. The .OBJ file is linked with the C0ROMX.OBJ startup code
  42. and with other .OBJ files (if needed; these may be from C code
  43. that has gone through the four above steps, or routines
  44. originally coded in assembly).  This produces a .EXE file.
  45.  
  46.     6. EXE2BIN converts the .EXE into a binary ROM image.
  47.  
  48.     7. INSJUMP inserts the reset jump into the ROM image.
  49.  
  50.     8. If necessary, the binary ROM image is converted into
  51. whatever format the particular EPROM programmer requires. (Mine
  52. is a plug-in card for the PC which takes binary format directly,
  53. so I don't have to do this step.)
  54.  
  55.     All these steps can be run in a .BAT or MAKE file, making the
  56. conversion of the original C code to ROM code almost automatic.
  57.  
  58. RULES AND ASSUMPTIONS:
  59.  
  60.     The rules and assumptions for writing ROMable code using this
  61. scheme are as follows:
  62.  
  63.     1. The Compact memory model (small code, large data) is used.
  64. This results in a maximum of 64K of code and initialized static
  65. variables (i.e. constants) but up to 1 MB of Data space.
  66.  
  67.     2. Initialized static variables are considered constants, and
  68. may not be changed during program execution because they are
  69. stored in ROM.
  70.  
  71.     3. Variables defined as "extern" are considered constants.
  72. Such variables would be used for font tables, for example.  True
  73. "variables" (which change during program execution) may not be
  74. defined in one C module and declared as "extern" in another.  The
  75. only means of inter-module variable passing is through function
  76. parameters and return values (a good programming practice
  77. anyway). IT IS OK to define variables in a C module and reference
  78. these as EXTRN's in assembler-coded modules, however.
  79.  
  80.     4. Most of the Runtime Library routines may NOT be used,
  81. because they have been compiled with the wrong segment groupings
  82. for the ROM environment.  Many of the library routines are not
  83. usable, anyway, because they pertain to the DOS environment and
  84. use system facilities that are not available in the ROM
  85. environment.  Library routines that are needed will have to be
  86. recreated, either in C or assembly.  You may want to order the
  87. Turbo C Runtime Library from Borland as a starting point. I
  88. ordered the Runtime Library several months into the project that
  89. stimulated the creation of this system, but I didn't really need
  90. it.
  91.  
  92.     I was able to compile and debug over 90% of my code (which
  93. was about 3,500 lines of C) using the Integrated Environment on a
  94. PC, then re-compile (with #DEFINES and #IFDEFs to accommodate the
  95. environmental differences) for ROM with little further debugging.
  96.  
  97. SEGMENT USAGE IN TURBO C:
  98.  
  99.     Turbo C assigns uninitialized static variables to a segment
  100. called _BSS and initialized static variables (i.e., constants) to
  101. a segment called _DATA. These two segments are associated via a
  102. GROUP directive, and assumed, under normal circumstances, to be
  103. jointly addressable by the DS register.  Instructions are
  104. generated in the _TEXT segment, and assumed addressable by the CS
  105. register.
  106.  
  107.     In the ROM environment, the constants in the _DATA segment
  108. and the code in the _TEXT segment are both located in the ROM and
  109. addressed by CS.  Only BSS is addressed by DS.  Therefore to
  110. convert the code generated by the compiler, ROMIZE is used as
  111. follows:
  112.  
  113. WHAT ROMIZE DOES:
  114.  
  115.     ROMIZE makes two passes through the assembly source code
  116. generated by the command line compiler.  In the first pass, it
  117. reads the code and accumulates a table of all symbols defined in
  118. the _DATA segment.  There are only a few such symbols, since
  119. Turbo C places all string constants in a single symbol "s@", and
  120. refers to individual strings as "s@+n".  Likewise, all
  121. non-string constants are part of a single symbol called "d@" and
  122. referred to by their displacements.  There are however, a large
  123. number of references to these symbols in the code.
  124.  
  125.     In its second pass, ROMIZE re-reads the source code and
  126. writes it out with modifications.  The modifications are:
  127.  
  128.     1. The command that groups _BSS and DATA together into DGROUP
  129. is changed so that only _BSS is in DGROUP.
  130.  
  131.     2. A new GROUP command is inserted creating CGROUP as _TEXT
  132. and _DATA.
  133.  
  134.     3. The ASSUME statement is changed from
  135.         ASSUME cs:_TEXT,ds:DGROUP
  136.           to:
  137.         ASSUME cs:CGROUP,ds:DGROUP
  138.  
  139.     4. In any line of code with a reference to a symbol in the
  140. _DATA segment, the group reference is changed from DGROUP to
  141. CGROUP, or a CGROUP reference is added if no group name is
  142. present.
  143.  
  144.     5. Preceding a line of code with a reference to a symbol in
  145. the _DATA segment may be a "mov x,ds" or a "push ds"instruction.
  146. This is changed to a "mov x,cs" or "push cs".
  147.  
  148.     6. Following a line of code with a reference to a symbol in
  149. the _DATA segment may be a "push ds" instruction. This is changed
  150. to a "push cs".
  151.  
  152.     In order to do the looking backward and forward, ROMIZE
  153. maintains a three-line pipeline, reading new lines into the head
  154. of the pipe and writing lines from the end.
  155.  
  156.     The various segment naming options of the compiler do not
  157. produce the desired changes for the ROM environment. I would be
  158. happy to pursue this subject further with anyone who is curious
  159. about it.
  160.  
  161. LINKING:
  162.  
  163.     You can use either TLINK or MS-LINK. The command line will,
  164. of course be different for each. The "Warning - no stack segment"
  165. will be produced by either. This is normal.
  166.  
  167.     You must read the link map carefully to verify that your code
  168. fit in the size of ROM you use. Consider the following partial
  169. link map:
  170.  
  171. LINK : warning L4021: no stack segment
  172.  
  173.  Start  Stop   Length Name                   Class
  174.  00000H 05A74H 05A75H _TEXT                  CODE
  175.  05A80H 07DF7H 02378H _DATA                  DATA
  176.  07DF8H 0A530H 02739H _BSS                   BSS
  177.  0A531H 0A531H 00000H _BSSEND                BSSEND
  178.  
  179.  Origin   Group
  180.  0000:0   CGROUP
  181.  07DF:0   DGROUP
  182.  
  183.     The .bin file that came out of EXE2BIN for the module that
  184. produced this link map was A531 (hex) bytes long, however, this
  185. module does fit in 32K. The key indicator is the sum of the
  186. lengths of _TEXT and _DATA, and consequently, the starting
  187. address of DGROUP (_BSS). Here it is 7DF8, about 500 bytes
  188. under 32K.
  189.  
  190. MORE ABOUT EXE2BIN:
  191.  
  192.     EXE2BIN is needed to convert the .EXE file into a binary
  193. image that can be transmitted to an EPROM programmer. It may need
  194. to perform "segment fixups" (segment relocation), if your program
  195. includes constructs like the following:
  196.  
  197.   static char *menu[] = {"Preview   ","Add Before","Add After "};
  198.  
  199.     This is a convenient way to generate arrays of strings for
  200. such things as menus.  This generates an array of pointers whose
  201. segment parts must be relocated by EXE2BIN to the segment address
  202. of the ROM.  Therefore, when the EXE2BIN step is run, you will be
  203. prompted to enter a segment address (F800 hex in the attached
  204. example).
  205.  
  206.     For those users of versions of DOS (such as 3.3) that do not
  207. include EXE2BIN, there are several alternatives: Use EXE2BIN from
  208. DOS 2.0 (I think 2.1 will work also), or use EXE2BIN from DOS 3.0
  209. or 3.1 and patch it so it does not test for DOS version. The test
  210. is at the beginning of these programs and is easily defeated.
  211. Except for the version check, I know of no DOS version
  212. dependencies in these programs. (I did all my development under
  213. DOS 3.2, however,  which still includes EXE2BIN).
  214.  
  215.     The EXE2COM program available on CompuServe and many bulletin
  216. boards may work in place of EXE2BIN (I have not tested it),
  217. EXCEPT when segment relocation is required (i.e., you use the
  218. above-described construct).  It does not do segment relocation.
  219.  
  220.     The fact that EXE2BIN stops and prompts you for a segment
  221. relocation factor is an annoyance.  You can use the PC Magazine
  222. KEY-FAKE.COM (see PCMagnet) to supply the required value in a
  223. batch or MAKE file. If KEY-FAKE is used in a MAKE file, you must
  224. have invoked KEY-FAKE at least once (without arguments is fine)
  225. before running MAKE. KEY-FAKE installs as a TSR the first time
  226. it is invoked, and apparently, Turbo MAKE doesn't like programs
  227. that do this, so if you invoke KEY-FAKE in a MAKE file
  228. without it already being resident Turbo MAKE terminates with an
  229. error 768.
  230.  
  231. MORE HINTS:
  232.  
  233.     If you declare an array of strings as mentioned in the above
  234. discussion, the "static" storage class is not necessary if the
  235. array is defined outside of any function.  If such an array is
  236. defined as local to (within) a function, then be certain to add
  237. "static", otherwise, Turbo C generates code to copy the array
  238. into the function's local variable space (the stack). This is
  239. unnecessary if the array is indeed a constant.
  240.  
  241.     Although you won't be using many (any) library functions, you
  242. may still want to include a number of standard header files.
  243. The library functions you recreate ought to use the same
  244. definitions as the library versions, anyway.
  245.  
  246.     "dos.h" is an especially useful item.  Although chances are
  247. you won't be using any of the file I/O functions defined in it,
  248. it includes some macros and in-line functions that are EXTREMELY
  249. useful in the ROM environment such as MK_FP(), inport(),
  250. outport(), FP_SEG(), FP_OFF(), peek() and poke().
  251.  
  252. FLOATING POINT LIMITATIONS:
  253.  
  254.     If you need to use floating point arithmetic, you may have a
  255. problem. If your target hardware includes a numeric co-processor
  256. then you will have to order the Runtime Library and modify the
  257. 8x87 support routines to run in the ROM environment. If you don't
  258. have an 8x87 and still want to use floating point, you can't
  259. because the Runtime Library does not include all of the floating
  260. point emulator source code.
  261.  
  262. SAMPLE PROGRAMS:
  263.  
  264.     A simple C program which reads a value from an input port
  265. and writes a selected string to an output port is included. A
  266. Turbo Make file is provided to create the ROM from this code,
  267. which illustrates all of the necessary processing steps.
  268.  
  269. STARTUP CODE -- C0ROMX.ASM:
  270.  
  271.     This code is simple, compared to the regular Turbo C startup
  272. code.  About all that is required is to set up the DS, SS, and SP
  273. registers.  The particular values for these will depend upon the
  274. amount and address space of your RAM.  The code has an optional
  275. sequence to initialize static variables to 0, as is "normal" in
  276. C.  I did not use such initialization because my particular
  277. application had battery-backed-up static RAM. Note that
  278. C0ROMX.ASM does not have a starting address in its END statement.
  279. The actual code startup is supplied by a JMP inserted by INSJUMP.
  280. Note also that the linking step specifies C0ROMX as the first
  281. object module. That guarantees it will start at relative address
  282. 0000 in the ROM.
  283.  
  284. INSJUMP.C:
  285.  
  286.     The 8088 family processors start up after a RESET by
  287. executing code beginning at FFFF:0. INSJUMP inserts an inter-
  288. segment jump to relative location 0 at the ROM location that
  289. corresponds to FFFF:0.  INSJUMP can process 32K (i.e. 27256) or
  290. 64K (27512) EPROMs. It is used as follows:
  291.  
  292.   INSJUMP filename [64K]
  293.  
  294. If the second parameter "64K" is not present, INSJUMP assumes it
  295. is processing a 32K ROM. The JMP instruction is to F800:0, and
  296. it is inserted at location 7FF0 in the ROM. For a 64K ROM, the
  297. instruction is a JMP F000:0 inserted at ROM location FFF0.
  298.  
  299.     The .bin file generated by EXE2BIN may be less than 32K
  300. for a 32K ROM, or less than 64K, for a 64K ROM. INSJUMP
  301. extends the file out to 32K or 64K with FF's. Erased EPROMs
  302. contain all FF's, so unused locations will not have to be
  303. programmed, which will shorten programming time on most
  304. programmers.
  305.  
  306.     As supplied, INSJUMP also calculates a checksum and places
  307. it in the last two ROM bytes. My product has a self-test that
  308. includes ROM checksum on power-up. Other users may want to put
  309. additional data, such as a part number, in the ROM. This can be
  310. done with INSJUMP.
  311.  
  312. WRITING ASSEMBLY-LANGUAGE FUNCTIONS:
  313.  
  314.     The only differences in writing assembly-language functions
  315. for the ROM environment versus DOS are segment groupings. The
  316. Turbo C User's guide suggests compiling a small C module to
  317. assembler as a starting point.  For the ROM environment do the
  318. same thing, then process the assembler through ROMIZE.
  319.  
  320.     If your assembler functions have only a code segment called
  321. _TEXT, with no other segment definitions, and if they reference
  322. only local variables and parameters stored on the stack, then you
  323. can assemble them just once and use the identical .OBJ modules in
  324. both ROM and DOS versions of your main C program.
  325.  
  326. BINTOHEX.C:
  327.  
  328.     This program converts a binary ROM image to Intel Hex format.
  329. It is has not been tested.  If you need some other format, such
  330. as Data I/O, contact me.
  331.  
  332.  
  333.     This has been a simple treatment of a complicated subject.
  334. I have tried to include as much information as I could think of,
  335. but there are undoubtedly a lot of pitfalls I haven't covered.
  336. Anyone who wishes to pursue this subject further is urged to
  337. contact me:
  338.  
  339.     Bob Haas
  340.     P. O. Box 1455
  341.     Tualatin, OR   97062-9557
  342.     (503) 692-1593
  343.     CompuServe User # 70357,3530
  344.